iT邦幫忙

2024 iThome 鐵人賽

DAY 3
0

原文題目
Given an integer array nums of unique elements, return all possible subsets(the power set).

The solution set must not contain duplicate subsets. Return the solution in any order.

題目摘要

  1. 給定一個包含唯一元素的整數陣列 nums,要求返回所有可能的子集(包含空子集)。
  2. 回傳的子集不能重複,但可以任意順序返回。

Example 1:

Input: nums = [1,2,3]
Output: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

Example 2:

Input: nums = [0]
Output: [[],[0]]

解題思路一

  1. 創建結果陣列
    • 首先,創建一個用來儲存所有子集的結果陣列 result
    • 因為空集也是題目所球的子集,所以一開始將空集 [] 加入 result
  2. 呼叫遞迴函式
    • 使用 backTracking 函式來生成所有可能的子集。
    • backTracking 函式的主要目的是透過遞迴探索每一個元素的可能性,逐一列出所有子集。
  3. 回溯過程
    • 遞迴函式從指定的 index 開始,逐個遍歷數組中的元素。
    • 在每次遞迴中,將當前元素 nums[i] 加入到當前的子集 path 中,然後將這個 path 複製一份加入到 result
    • 接著,遞迴呼叫 backTracking,進一步處理剩下的元素(從下一個索引開始),以生成更大的子集。
    • 當這一層遞迴完成後,進行回溯:將剛剛加入的元素從 path 中移除,恢復到上一層狀態,繼續探索其他可能的子集組合。
  4. 最終結果
    • 遞迴完成後,result 中將包含所有可能的子集,包括空集、單個元素的子集、以及包含多個元素的子集。

程式碼一

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        //創建一個儲存結果的陣列
        List<List<Integer>> result = new ArrayList<>();
        //一開始先將空集加入result
        result.add(new ArrayList<>());
        //呼叫backTracking函式
        backTracking(0, nums, new ArrayList<>(), result);
        return result;
    }

    private void backTracking(int index, int[] nums, List<Integer> path, List<List<Integer>> result){
        for(int i=index; i<nums.length; i++){
            path.add(nums[i]); //加入當前元素到path
            result.add(new ArrayList<>(path)); //把當前的path加入結果陣列
            //遞迴呼叫:將指標移至下一個元素並加入path
            backTracking(i+1, nums, path, result); 
            //進行回溯:將最後一次放進path的元素移除
            path.remove(path.size()-1);
        }
    }
}

解題思路二

  1. 設定串列:
    • 建立一個二維串列 result 來存儲所有的子集。
    • 建立一個串列 subset 用來儲存當前的子集。
  2. 遞迴處理每個元素
    • 定義遞迴函數 findSubset,參數包含數組 nums、當前處理的索引 index、結果集 result 和當前子集 subset
    • 如果當前索引 index 等於數組長度,表示已經處理完所有元素,此時將 subset 複製並加入到 result 中,然後返回。
  3. 包含當前元素的情況
    • nums[index] 加入到當前子集中。
    • 遞迴處理下一個元素,即調用 findSubset(nums, index + 1, result, subset)
  4. 排除當前元素的情況
    • 在遞迴處理完成後,撤銷之前的選擇,即從 subset 中移除剛才加入的元素。
    • 再次遞迴處理下一個元素,即調用 findSubset(nums, index + 1, result, subset),這次不包括當前元素。
  5. 遞迴終止條件
    • 當所有元素都被處理完時,即索引達到數組長度,遞迴結束。
  6. 返回結果
    • 完成所有遞迴後,將回傳包含所有可能子集的result

程式碼二

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> result = new ArrayList<>(); //設立結果的二維串列用來存儲所有子集
        List<Integer> subset = new ArrayList<>(); //設立串列用來儲存當前子集

        findSubset(nums, 0, result, subset);
        return result;        
    }

    //用來尋找所有子集的遞迴方法
    private void findSubset(int[] nums, int index, List<List<Integer>> result, List<Integer> subset) {
        //當前索引等於數組長度,表示已經處理完所有元素
        if (index == nums.length) {
            result.add(new ArrayList<>(subset));
            return;
        }

        // 選擇當前元素加入到當前子集中,並進行下一步遞迴
        subset.add(nums[index]);
        findSubset(nums, index + 1, result, subset);

        // 撤回選擇,移除當前數字,並遞迴檢查下一個數字
        subset.remove(subset.size() - 1);
        findSubset(nums, index + 1, result, subset);
    }    
}

結論
這題我和我的組員分別使用兩種解法,雖然都是利用遞迴來生成所有子集,但它們的實現方式有所不同。解法一是透過回溯法逐步將元素加入當前子集,並在每次遞迴中進行回溯來生成新的子集,結構上使用了for迴圈來控制遞迴的範圍,適合處理需要靈活控制的回溯問題。而解法二則是透過每個元素的二分選擇(包含或不包含當前元素)進行遞迴且沒有使用迴圈,適合處理需要遍歷所有選擇狀態的問題。整體而言,這兩種方法都能有效地解決問題,但根據具體需求,可以選擇不同的遞迴策略來生成子集。


上一篇
Day2 Backtracking回朔 - 題目1:39. Combination Sum
下一篇
Day4 Backtracking回朔 - 題目3:79. Word Search
系列文
Java刷題A:Leetcode Top 100 Liked13
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言